package com.socket.server.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import com.socket.core.constant.ChatConstants;
import com.socket.core.constant.Constants;
import com.socket.core.util.Enums;
import com.socket.secure.filter.SecureRequsetFilter;
import com.socket.server.custom.CustomRealm;
import com.socket.server.custom.RedisSessionDao;
import com.socket.server.model.enmus.HttpStatus;
import lombok.RequiredArgsConstructor;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticationFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Configuration
@RequiredArgsConstructor
public class ShiroConfig {
    private final RedisSessionDao redisSessionDao;
    private final SecureRequsetFilter secureFilter;
    private final ChatConstants constants;
    private final CustomRealm customRealm;

    @Bean
    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(getDefaultWebSecurityManager());
        Map<String, Filter> filters = bean.getFilters();
        filters.put("user", new JsonFilter());
        filters.put("anon", secureFilter::filter);
        Map<String, String> definitionMap = bean.getFilterChainDefinitionMap();
        definitionMap.put("/login/**", "anon");
        definitionMap.put("/qqlogin/**", "anon");
        definitionMap.put("/wxlogin/**", "anon");
        definitionMap.put("/secure/**", "anon");
        definitionMap.put("/**", "user");
        return bean;
    }

    @Bean
    public SecurityManager getDefaultWebSecurityManager() {
        DefaultWebSecurityManager security = new DefaultWebSecurityManager();
        security.setSessionManager(sessionManager());
        security.setRealm(customRealm);
        return security;
    }

    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        manager.setGlobalSessionTimeout(Constants.SESSION_VALID_TIME * 1000);
        manager.setSessionValidationInterval(TimeUnit.HOURS.toMillis(1));
        manager.setSessionValidationSchedulerEnabled(true);
        manager.setSessionIdCookie(sessionIdCookie());
        manager.setSessionIdCookieEnabled(true);
        manager.setSessionIdUrlRewritingEnabled(false);
        manager.setSessionDAO(redisSessionDao);
        return manager;
    }

    @Bean
    public SimpleCookie sessionIdCookie() {
        SimpleCookie cookie = new SimpleCookie("SESSION");
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        cookie.setMaxAge((int) Constants.SESSION_VALID_TIME);
        return cookie;
    }

    class JsonFilter extends AuthenticationFilter {
        @Override
        protected void executeChain(ServletRequest request, ServletResponse response, FilterChain chain) throws Exception {
            secureFilter.filter(request, response, chain);
        }

        @Override
        protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
            Subject subject = SecurityUtils.getSubject();
            // 来自Cookie
            if (subject.isRemembered()) {
                return true;
            }
            // 来自其他服务
            String header = WebUtils.toHttp(request).getHeader(constants.getOauthToken());
            if (StrUtil.equals(header, constants.getOauthTokenValue())) {
                return true;
            }
            // 返回403
            WebUtils.toHttp(response).setStatus(403);
            response.setContentType(ContentType.JSON.toString(StandardCharsets.UTF_8));
            response.getWriter().write(Enums.toJSON(HttpStatus.UNAUTHORIZED.message("登录信息失效")));
            return false;
        }
    }
}
